interface Cancellable {
    cancel(): void;
}

export class UserEventSource<EventMap> {

    private listenerGroups = new Map<string, ((this: this, ev: any) => any)[]>()

    public addEventListener<K extends keyof EventMap>(type: K,
                                        listener: (this: this, event: EventMap[K]) => any): Cancellable {
        let key = type as string;
        let listeners = this.listenerGroups.get(key) || [];
        listeners.push(listener);
        this.listenerGroups.set(key, listeners);
        return { cancel:() => this.removeEventListener(type, listener) }
    }
    public removeEventListener<K extends keyof EventMap>(type: K, listener: (this: this, event: EventMap[K]) => any): void {
        let key = type as string;
        let listeners = this.listenerGroups.get(key);
        if (!listeners) return;
        let targetIdx = listeners.findIndex((value => value === listener));
        if (targetIdx >= 0) {
            listeners.splice(targetIdx,1);
            this.listenerGroups.set(key, listeners);
        }
    }

    public on<K extends keyof EventMap>(type: K,
                                        listener: (this: this, event: EventMap[K]) => any,
                                        until?: (this: this, event: EventMap[K]) => boolean): Cancellable {
        const f =( event: EventMap[K]) => {
            if (until?.apply(this,[event])) {
                this.removeEventListener(type,f);
            }else {
                listener.apply(this,[event]);
            }
        }
        return this.addEventListener(type, f);
    }

    public once<K extends keyof EventMap>(type: K,
                                          listener: (this: this, event: EventMap[K]) => any,
                                          which?: (this: this, event: EventMap[K]) => boolean): Cancellable  {
        const f =( event: EventMap[K]) => {
            if (which ? which.apply(this,[event]) : true) {
                listener.apply(this,[event])
                this.removeEventListener(type,f)
            }
        }
        return this.addEventListener(type, f);
    }

    public off<K extends keyof EventMap>(type: K, listener?: (this: this, event: EventMap[K]) => any): void {
        if (listener) {
            this.removeEventListener(type, listener);
        }else {
            let key = type as string;
            this.listenerGroups.set(key, []);
        }
    }

    protected dispatchEvent<K extends keyof EventMap>(type: K, event: EventMap[K]): void {
        let key = type as string;
        let listeners = this.listenerGroups.get(key) || [];
        Array.from(listeners) //防止监听器对listeners进行突变而导致遍历出错;
            .forEach((listener) => listener.apply(this,[event]))
    }
}